﻿/*
    * Designer:       Virgil  
    * Description:    The data structure of the Sudoku
    * Created:         06/07/2010
    * History:
==============================================================
    * Author      DateTime        Alter Description
==============================================================
    */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace SudokuWPF
{
    /// <summary>
    /// Cell类，定义了数格
    /// </summary>
    public class Cell : INotifyPropertyChanged  //实现接口，当某一属性值已更改时发出通知
    {
        #region 字段
        bool readOnlyValue = false;                     //只读数据，判断用
        int? valueValue = null;                         //存放数字，可以为空值
        ObservableCollection<int?> possibleValuesValue; //存放候选数的集合，ObservableCollection在集合添加项或移除项，可提供通知
        bool isValidValue = true;                       //判断值是否成立
        #endregion 

        #region 属性
        public bool ReadOnly
        {
            get
            {
                return readOnlyValue;
            }
            set
            {
                if (readOnlyValue != value)
                {
                    readOnlyValue = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ReadOnly"));//PropertyChanged通知ReadOnly的属性值发生改变
                }
            }
        }
        public int? Value
        {
            get
            {
                return valueValue;
            }
            set
            {
                if (valueValue != value)
                {
                    valueValue = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Value"));
                }
            }
        }
        public ObservableCollection<int?> PossibleValues
        {
            get
            {
                return possibleValuesValue;
            }
        }
        public bool IsValid
        {
            get
            {
                return isValidValue;
            }
            set
            {
                if (isValidValue != value)
                {
                    isValidValue = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("IsValid"));
                }
            }
        }
        #endregion

        #region 数格构造函数
        public Cell(int maxval)
        {
            possibleValuesValue = new ObservableCollection<int?>();
            possibleValuesValue.Add(null);
            for (int i = 1; i <= maxval; i++)   
            {
                possibleValuesValue.Add(i); //添加所有可能的数字
            }
        }
        #endregion

        #region INotifyPropertyChanged 成员
        //处理PropertyChanged事件
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
    /// <summary>
    /// Box类，定义宫格
    /// </summary>
    public class Box : INotifyPropertyChanged
    {
        #region 字段
        ObservableCollection<ObservableCollection<Cell>> Rows;  //存放宫格的值
        bool isValidValue = true;       //判断值是否成立
        #endregion

        #region 属性
        public ObservableCollection<ObservableCollection<Cell>> GridRows
        {
            get
            {
                return Rows;
            }
        }
        public bool IsValid
        {
            get
            {
                return isValidValue;
            }
        }
        public Cell this[int row, int col]  //索引
        {
            get
            {
                if (row < 0 || row >= Rows.Count) throw new ArgumentOutOfRangeException("row", row, "非法行索引");
                if (col < 0 || col >= Rows.Count) throw new ArgumentOutOfRangeException("col", col, "非法列索引");
                return Rows[row][col];
            }
        }
        #endregion

        #region 方法
        //宫格构造函数，向两层ObservableCollection容器中加入数格
        public Box(int size)
        {
            Rows = new ObservableCollection<ObservableCollection<Cell>>();  
            for (int i = 0; i < size; i++)
            {
                ObservableCollection<Cell> Col = new ObservableCollection<Cell>();
                for (int j = 0; j < size; j++)
                {
                    Cell c = new Cell(size * size); //初始化数格
                    c.PropertyChanged += new PropertyChangedEventHandler(c_PropertyChanged);
                    Col.Add(c);
                }
                Rows.Add(Col);
            }
        }
        //检查宫格里面是否有重复的数字
        private bool CheckIsValid()
        {
            bool[] used = new bool[Rows.Count * Rows.Count];
            foreach (ObservableCollection<Cell> r in Rows)
            {
                foreach (Cell c in r)   //遍历每个宫格
                {
                    if (c.Value.HasValue)   //判断该数格是否包含已有的值
                    {
                        if (used[c.Value.Value - 1])
                        {
                            return false;       
                        }
                        else
                        {
                            used[c.Value.Value - 1] = true;
                        }
                    }
                }
            }
            return true;
        }
        //属性值发生变化时的处理
        void c_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Value")  //如果数值发生改变
            {
                bool valid = CheckIsValid();//检查数值是否成立
                foreach (ObservableCollection<Cell> r in Rows)
                {
                    foreach (Cell c in r)
                    {
                        c.IsValid = valid;
                    }
                }

                isValidValue = valid;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsValid"));//通知IsValid的值发生改变
            }
        }
        #endregion

        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
    /// <summary>
    /// Board类，定义整个数独棋盘
    /// </summary>
    public class Board : INotifyPropertyChanged
    {
        #region 字段
        ObservableCollection<ObservableCollection<Box>> Rows;
        bool isValidValue = true;       //用于判断填入的数是否成立
        bool isFullValue = false;       //用于判读是否数格填满了
        #endregion

        #region 属性
        public ObservableCollection<ObservableCollection<Box>> GridRows
        {
            get
            {
                return Rows;
            }
        }
        public int Size
        {
            get
            {
                return Rows.Count * Rows.Count;
            }
        }
        public bool IsValid
        {
            get
            {
                return isValidValue;
            }
        }
        public bool IsFull
        {
            get
            {
                return isFullValue;
            }
        }
        public Cell this[int row, int col]
        {
            get
            {
                int totalsize = Rows.Count * Rows.Count;
                if (row < 0 || row >= totalsize) throw new ArgumentOutOfRangeException("row", row, "非法行索引");
                if (col < 0 || col >= totalsize) throw new ArgumentOutOfRangeException("col", col, "非法类索引");
                return Rows[row / Rows.Count][col / Rows.Count][row % Rows.Count, col % Rows.Count];
            }
        }
        #endregion

        #region 方法
        //棋盘构造函数
        public Board(int totalsize)
        {
            int size = (int)Math.Sqrt(totalsize);
            Rows = new ObservableCollection<ObservableCollection<Box>>();
            for (int i = 0; i < size; i++)
            {
                ObservableCollection<Box> Col = new ObservableCollection<Box>();
                for (int j = 0; j < size; j++)
                {
                    Box g = new Box(size);
                    g.PropertyChanged += new PropertyChangedEventHandler(g_PropertyChanged);
                    Col.Add(g);
                }
                Rows.Add(Col);
            }
        }
        //检查行是否成立
        private bool CheckRowIsValid(int row)
        {
            int width = Rows.Count * Rows.Count;
            bool[] used = new bool[width];
            for (int i = 0; i < width; i++)
            {
                Cell c = this[row, i];
                if (c.Value.HasValue)   //检查是否有重复的数字
                {
                    if (used[c.Value.Value - 1])
                    {
                        return false;
                    }
                    else
                    {
                        used[c.Value.Value - 1] = true;
                    }
                }
            }
            return true;
        }
        //检查列是否成立
        private bool CheckColumnIsValid(int col)
        {
            int height = Rows.Count * Rows.Count;
            bool[] used = new bool[height];
            for (int i = 0; i < height; i++)
            {
                Cell c = this[i, col];
                if (c.Value.HasValue)
                {
                    if (used[c.Value.Value - 1])
                    {
                        return false;
                    }
                    else
                    {
                        used[c.Value.Value - 1] = true;
                    }
                }
            }
            return true;
        }
        //属性值发生变化时的处理
        void g_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsValid")
            {
                bool valid = true;
                int totalsize = Rows.Count * Rows.Count;
                isFullValue = true;
                for (int i = 0; i < totalsize; i++)
                {
                    bool rowValid = CheckRowIsValid(i);
                    bool[] colValid = new bool[totalsize];
                    for (int j = 0; j < totalsize; j++)
                    {
                        if (this[i, j].Value == null)
                            isFullValue = false;
                        colValid[j] = CheckColumnIsValid(j);
                    }
                    for (int j = 0; j < totalsize; j++)
                    {
                        bool boxValid = Rows[i / Rows.Count][j / Rows.Count].IsValid;
                        //判断是否同时满足行列和宫格都成立了
                        if (!colValid[j] || !rowValid || !boxValid)
                            valid = false;
                        this[i, j].IsValid = colValid[j] & rowValid & boxValid;
                    }
                }
                isValidValue = valid;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsValid"));
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsFull"));
            }
        }
        /// <summary>
        /// 随即生成游戏的方法
        /// </summary>
        /// <param name="givens">需要填充的数格个数</param>
        public void GenerateGame(int givens)
        {
            int size = Rows.Count * Rows.Count;
            //判断填充个数是否合法
            if (givens < 0 || givens > size * size)
            {
                throw new ArgumentOutOfRangeException("givens", givens, "给定的生成已知数数目数目不合法");
            }
            for (int r = 0; r < size; r++)
            {
                for (int c = 0; c < size; c++)
                {
                    Cell cell = this[r, c];
                    cell.ReadOnly = false;
                    cell.Value = null;
                }
            }
            Random rnd = new Random();//表示伪随机数生成器
            for (int i = 0; i < givens; i++)
            {
                bool givenFound;    //标识是否生成完成
                do
                {
                    givenFound = false;
                    Cell cell = null;
                    do
                    {
                        int row = rnd.Next(size);
                        int col = rnd.Next(size);
                        cell = this[row, col];//随机单元格
                    }
                    while (cell.Value != null);//当所选单元格是不为空
                    int baseVal = rnd.Next(size) + 1;//产生1-9的随机数
                    for (int v = 0; v < size; v++)
                    {
                        cell.Value = baseVal;
                        if (this.IsValid)    //如果这个数成立，则标识为只读 
                        {
                            cell.ReadOnly = true;
                            givenFound = true;
                            break;
                        }
                        baseVal++;
                        if (baseVal > size) 
                            baseVal = 1;//当baseVal数大于宫格的尺寸，置一
                    }
                    if (!givenFound) 
                        cell.Value = null;
                }
                while (!givenFound);
            }

        }
        //使用解题器的随机生成方法
        /// <summary>
        /// 通过解题器生成游戏
        /// </summary>
        public void GenerateGameUsingSolver(ISudokuSolver s, int givens)
        {
            int size = Rows.Count * Rows.Count;
            if (givens < 0 || givens > size * size)
            {
                throw new ArgumentOutOfRangeException("givens", givens, "给定的生成已知数数目数目不合法");
            }
            for (int r = 0; r < size; r++)
            {
                for (int c = 0; c < size; c++)
                {
                    Cell cell = this[r, c];
                    cell.ReadOnly = false;
                    cell.Value = null;
                }
            }
            Random rnd = new Random();
            int row = rnd.Next(size);
            int col = rnd.Next(size);
            this[row, col].Value = rnd.Next(size) + 1;
            int?[,] a = this.ToArray();//将谜题放入数组
            s.Solve(ref a); //判断能否求得谜题答案
            this.FromArray(a);
            for (int i = 0; i < (size * size) - givens; i++) //挖掉数字
            {
                for (; ; )
                {
                    int r = rnd.Next(size);
                    int c = rnd.Next(size);
                    Cell cell = this[r, c];
                    if (!cell.Value.HasValue)//判断是否为空值
                        continue;
                    cell.Value = null;
                    cell.ReadOnly = false;
                    break;
                }
            }
            for (int r = 0; r < size; r++)
            {
                for (int c = 0; c < size; c++)
                {
                    Cell cell = this[r, c];
                    if (cell.Value.HasValue)
                        cell.ReadOnly = true;
                }
            }
        }
        //将谜题存入数组中
        public int?[,] ToArray()
        {
            int?[,] t = new int?[this.Size, this.Size];
            for (int r = 0; r < this.Size; r++)
            {
                for (int c = 0; c < this.Size; c++)
                {
                    Cell cell = this[r, c];
                    if (cell.ReadOnly)
                    {
                        t[r, c] = -cell.Value;
                    }
                    else
                    {
                        t[r, c] = cell.Value;
                    }
                }
            }
            return t;
        }
        //从数组中读取出谜题
        public void FromArray(int?[,] t)
        {
            if (t.GetLength(0) != t.GetLength(1) || t.GetLength(0) != this.Size)
            {
                throw new ArgumentOutOfRangeException("t", t, "数组的大小不正确");
            }
            for (int r = 0; r < this.Size; r++)
            {
                for (int c = 0; c < this.Size; c++)
                {
                    if (t[r, c] < 0)
                    {
                        this[r, c].Value = -t[r, c];
                        this[r, c].ReadOnly = true;
                    }
                    else
                    {
                        this[r, c].Value = t[r, c];
                    }
                }
            }
        }

        //创建游戏
        public void CreateGame()
        {
            int size = Rows.Count * Rows.Count;
            for (int r = 0; r < size; r++)
            {
                for (int c = 0; c < size; c++)
                {
                    Cell cell = this[r, c];
                    cell.ReadOnly = false;
                    cell.Value = null;
                }
            }
        }
        //创建游戏完毕
        public void CreateGameOver()
        {
            int size = Rows.Count * Rows.Count;
            for (int r = 0; r < size; r++)
            {
                for (int c = 0; c < size; c++)
                {
                    Cell cell = this[r, c];
                    if (cell.Value.HasValue)
                        cell.ReadOnly = true;
                }
            }
        }
        #endregion

        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
